﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Interception;
using MugenInjection.Interception.Interface;
using MugenInjection.Interception.Proxy;
using MugenInjection.Parameters;
using Should;

namespace MugenInjectionTest.Interception
{
    public class InterceptEventClass
    {
        public virtual event Action TestEvent;

        public Delegate[] Delegates
        {
            get
            {
                if (TestEvent == null)
                    return null;
                return TestEvent.GetInvocationList();
            }
        }
    }

    public class InterceptPropertyClass
    {
        public virtual object Property { get; set; }
    }

    public class InterceptMethodClass
    {
        public const string Value = "Method";

        public virtual string Method(Action input1, object input2)
        {
            return Value;
        }
    }

    public class InterceptorProcessEvent : IInterceptorProcess
    {
        #region Properties


        public bool ProcessOriginal { get; set; }
        public bool ProcessInTarget { get; set; }
        public Delegate AddEventDelegate { get; set; }
        public Delegate RemoveEventDelegate { get; set; }

        #endregion

        #region Implementation of IInterceptorProcess

        /// <summary>
        /// Intercept method.
        /// </summary>
        /// <param name="methodInterceptor">The specified <see cref="IMethodInterceptor"/>.</param>
        public void InterceptMethod(IMethodInterceptor methodInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept get property method.
        /// </summary>
        /// <param name="propertyGetInterceptor">The specified <see cref="IPropertyGetInterceptor"/></param>
        public void InterceptGetProperty(IPropertyGetInterceptor propertyGetInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept set property method.
        /// </summary>
        /// <param name="propertySetInterceptor">The specified <see cref="IPropertySetInterceptor"/>.</param>
        public void InterceptSetProperty(IPropertySetInterceptor propertySetInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept add event method.
        /// </summary>
        /// <param name="eventAddInterceptor">The specified <see cref="IEventAddInterceptor"/>.</param>
        public void InterceptAddEvent(IEventAddInterceptor eventAddInterceptor)
        {
            AddEventDelegate = eventAddInterceptor.AddValue;
            if (ProcessOriginal)
                eventAddInterceptor.ProcessOriginal();
            if (ProcessInTarget)
                eventAddInterceptor.ProcessInTarget();
        }

        /// <summary>
        /// Intercept remove event method.
        /// </summary>
        /// <param name="eventRemoveInterceptor">The specified <see cref="IEventRemoveInterceptor"/>.</param>
        public void InterceptRemoveEvent(IEventRemoveInterceptor eventRemoveInterceptor)
        {
            RemoveEventDelegate = eventRemoveInterceptor.RemoveValue;
            if (ProcessOriginal)
                eventRemoveInterceptor.ProcessOriginal();
            if (ProcessInTarget)
                eventRemoveInterceptor.ProcessInTarget();
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class InterceptorProcessProperty : IInterceptorProcess
    {
        #region Properties

        public bool ProcessOriginal { get; set; }
        public bool ProcessInTarget { get; set; }
        public object SetValue { get; set; }
        public object GetValue { get; set; }

        #endregion

        #region Implementation of IInterceptorProcess

        /// <summary>
        /// Intercept method.
        /// </summary>
        /// <param name="methodInterceptor">The specified <see cref="IMethodInterceptor"/>.</param>
        public void InterceptMethod(IMethodInterceptor methodInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept get property method.
        /// </summary>
        /// <param name="propertyGetInterceptor">The specified <see cref="IPropertyGetInterceptor"/></param>
        public void InterceptGetProperty(IPropertyGetInterceptor propertyGetInterceptor)
        {
            propertyGetInterceptor.GetValue = GetValue;
            if (ProcessOriginal)
                propertyGetInterceptor.ProcessOriginal();
            if (ProcessInTarget)
                propertyGetInterceptor.ProcessInTarget();
        }

        /// <summary>
        /// Intercept set property method.
        /// </summary>
        /// <param name="propertySetInterceptor">The specified <see cref="IPropertySetInterceptor"/>.</param>
        public void InterceptSetProperty(IPropertySetInterceptor propertySetInterceptor)
        {
            SetValue = propertySetInterceptor.SetValue;
            if (ProcessOriginal)
                propertySetInterceptor.ProcessOriginal();
            if (ProcessInTarget)
                propertySetInterceptor.ProcessInTarget();
        }

        /// <summary>
        /// Intercept add event method.
        /// </summary>
        /// <param name="eventAddInterceptor">The specified <see cref="IEventAddInterceptor"/>.</param>
        public void InterceptAddEvent(IEventAddInterceptor eventAddInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept remove event method.
        /// </summary>
        /// <param name="eventRemoveInterceptor">The specified <see cref="IEventRemoveInterceptor"/>.</param>
        public void InterceptRemoveEvent(IEventRemoveInterceptor eventRemoveInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class InterceptorProcessMethod : IInterceptorProcess
    {
        #region Properties

        public bool ProcessOriginal { get; set; }
        public bool ProcessInTarget { get; set; }

        public object[] MethodObjects { get; set; }

        public object Result { get; set; }

        #endregion

        #region Implementation of IInterceptorProcess

        /// <summary>
        /// Intercept method.
        /// </summary>
        /// <param name="methodInterceptor">The specified <see cref="IMethodInterceptor"/>.</param>
        public void InterceptMethod(IMethodInterceptor methodInterceptor)
        {
            MethodObjects = methodInterceptor.InputParameters;
            methodInterceptor.ReturnValue = Result;
            if (ProcessOriginal)
                methodInterceptor.ProcessOriginal();
            if (ProcessInTarget)
                methodInterceptor.ProcessInTarget();
        }

        /// <summary>
        /// Intercept get property method.
        /// </summary>
        /// <param name="propertyGetInterceptor">The specified <see cref="IPropertyGetInterceptor"/></param>
        public void InterceptGetProperty(IPropertyGetInterceptor propertyGetInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept set property method.
        /// </summary>
        /// <param name="propertySetInterceptor">The specified <see cref="IPropertySetInterceptor"/>.</param>
        public void InterceptSetProperty(IPropertySetInterceptor propertySetInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept add event method.
        /// </summary>
        /// <param name="eventAddInterceptor">The specified <see cref="IEventAddInterceptor"/>.</param>
        public void InterceptAddEvent(IEventAddInterceptor eventAddInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Intercept remove event method.
        /// </summary>
        /// <param name="eventRemoveInterceptor">The specified <see cref="IEventRemoveInterceptor"/>.</param>
        public void InterceptRemoveEvent(IEventRemoveInterceptor eventRemoveInterceptor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class InterceptorPriority : IInterceptorProcess
    {
        public Action<IList<IInterceptorProcess>> Action { get; set; }

        #region Implementation of IInterceptorProcess

        /// <summary>
        /// Intercept method.
        /// </summary>
        /// <param name="methodInterceptor">The specified <see cref="IMethodInterceptor"/>.</param>
        public void InterceptMethod(IMethodInterceptor methodInterceptor)
        {
            Action(methodInterceptor.Interceptors);
        }

        /// <summary>
        /// Intercept get property method.
        /// </summary>
        /// <param name="propertyGetInterceptor">The specified <see cref="IPropertyGetInterceptor"/></param>
        public void InterceptGetProperty(IPropertyGetInterceptor propertyGetInterceptor)
        {
        }

        /// <summary>
        /// Intercept set property method.
        /// </summary>
        /// <param name="propertySetInterceptor">The specified <see cref="IPropertySetInterceptor"/>.</param>
        public void InterceptSetProperty(IPropertySetInterceptor propertySetInterceptor)
        {
        }

        /// <summary>
        /// Intercept add event method.
        /// </summary>
        /// <param name="eventAddInterceptor">The specified <see cref="IEventAddInterceptor"/>.</param>
        public void InterceptAddEvent(IEventAddInterceptor eventAddInterceptor)
        {
        }

        /// <summary>
        /// Intercept remove event method.
        /// </summary>
        /// <param name="eventRemoveInterceptor">The specified <see cref="IEventRemoveInterceptor"/>.</param>
        public void InterceptRemoveEvent(IEventRemoveInterceptor eventRemoveInterceptor)
        {
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; set; }

        #endregion
    }

    public class DefaultInterceptorAction : ISimpleInterceptorProcess
    {

        #region Implementation of ISimpleInterceptorProcess

        /// <summary>
        /// Intercept all action.
        /// </summary>
        /// <param name="methodInterceptor"></param>
        public void Intercept(IInterceptor methodInterceptor)
        {
            methodInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class DefaultInterceptorActionClass
    {
        public readonly string St;
        public readonly int I;

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        public DefaultInterceptorActionClass()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        public DefaultInterceptorActionClass(string st, int i)
        {
            St = st;
            I = i;
        }

        #endregion

        #region Static fields

        public static int DefaultValue = 8999;
        public static string DefaultStValue = "dsg";

        #endregion

        #region Methods

        public int CallProtectedMethod(ref int value)
        {
            var i = value;
            value = DefaultValue;
            return i;
        }

        public virtual string PublicMethod(string st)
        {
            return st;
        }

        internal virtual string InternalMethod(ref string value)
        {
            var i = value;
            value = DefaultStValue;
            return i;
        }

        protected virtual int ProtectedMethod(ref int value)
        {
            var i = value;
            value = DefaultValue;
            return i;
        }

        #endregion

        #region Property

        public string ProtectedPropertyAccess
        {
            get { return ProtectedProperty; }
            set { ProtectedProperty = value; }
        }

        public virtual string Property { get; set; }

        protected virtual string ProtectedProperty { get; set; }

        internal virtual int InternalProperty { get; set; }

        #endregion

        #region Events

        public Delegate[] PublicEventDelegates
        {
            get
            {
                if (PublicEvent == null)
                    return null;
                return PublicEvent.GetInvocationList();
            }
        }

        public Delegate[] ProtectedEventDelegates
        {
            get
            {
                if (ProtectedEvent == null)
                    return null;
                return ProtectedEvent.GetInvocationList();
            }
        }

        public Delegate[] InternalEventDelegates
        {
            get
            {
                if (InternalEvent == null)
                    return null;
                return InternalEvent.GetInvocationList();
            }
        }

        public event Action ProtectedEventAccess
        {
            add { ProtectedEvent += value; }
            remove { ProtectedEvent -= value; }
        }

        public virtual event Action PublicEvent;

        protected virtual event Action ProtectedEvent;

        internal virtual event Action InternalEvent;


        #endregion

    }

    public class TargetInterceptor : ISimpleInterceptorProcess
    {
        public readonly List<object> Targets = new List<object>();

        #region Implementation of ISimpleInterceptorProcess

        /// <summary>
        /// Intercept all action.
        /// </summary>
        /// <param name="methodInterceptor"/>
        public void Intercept(IInterceptor methodInterceptor)
        {
            Targets.Add(methodInterceptor.Target);
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class SimpleInterceptor : IInterceptorProcess
    {
        public bool IsNoOpBehavior { get; set; }

        #region Implementation of IInterceptorProcess

        /// <summary>
        /// Intercept method.
        /// </summary>
        /// <param name="methodInterceptor">The specified <see cref="T:MugenInjection.Interception.Interface.IMethodInterceptor"/>.</param>
        public void InterceptMethod(IMethodInterceptor methodInterceptor)
        {
            if (IsNoOpBehavior) return;
            methodInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Intercept get property method.
        /// </summary>
        /// <param name="propertyGetInterceptor">The specified <see cref="T:MugenInjection.Interception.Interface.IPropertyGetInterceptor"/></param>
        public void InterceptGetProperty(IPropertyGetInterceptor propertyGetInterceptor)
        {
            if (IsNoOpBehavior) return;
            propertyGetInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Intercept set property method.
        /// </summary>
        /// <param name="propertySetInterceptor">The specified <see cref="T:MugenInjection.Interception.Interface.IPropertySetInterceptor"/>.</param>
        public void InterceptSetProperty(IPropertySetInterceptor propertySetInterceptor)
        {
            if (IsNoOpBehavior) return;
            propertySetInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Intercept add event method.
        /// </summary>
        /// <param name="eventAddInterceptor">The specified <see cref="T:MugenInjection.Interception.Interface.IEventAddInterceptor"/>.</param>
        public void InterceptAddEvent(IEventAddInterceptor eventAddInterceptor)
        {
            if (IsNoOpBehavior) return;
            eventAddInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Intercept remove event method.
        /// </summary>
        /// <param name="eventRemoveInterceptor">The specified <see cref="T:MugenInjection.Interception.Interface.IEventRemoveInterceptor"/>.</param>
        public void InterceptRemoveEvent(IEventRemoveInterceptor eventRemoveInterceptor)
        {
            if (IsNoOpBehavior) return;
            eventRemoveInterceptor.ProcessOriginal();
        }

        /// <summary>
        /// Interceptor priority.
        /// </summary>
        public int Priority { get; private set; }

        #endregion
    }

    public class InterceptTarget
    {
        public readonly Guid Id = Guid.NewGuid();

        public virtual string Property { get; set; }

        public virtual Action Event { get; set; }

        public Delegate[] GetEvents()
        {
            if (Event == null)
                return null;
            return Event.GetInvocationList();
        }

        public virtual string Method(string st)
        {
            return st;
        }
    }

    [TestClass]
    public class InterceptorProcessTest : InterceptionTestBase
    {
        [TestMethod]
        public void InterceptEventTest()
        {
            using (var injector = GetInjector())
            {
                var interceptorProcessEvent = new InterceptorProcessEvent { ProcessOriginal = true };
                InterceptionExtensionUtils.Intercept(injector.Bind<InterceptEventClass>(), interceptorProcessEvent);

                var interceptEventClass = injector.Get<InterceptEventClass>();
                var action = new Action(() => { });
                interceptEventClass.TestEvent += action;
                action.ShouldEqual(interceptorProcessEvent.AddEventDelegate);
                interceptorProcessEvent.RemoveEventDelegate.ShouldBeNull();
                interceptEventClass.Delegates.Length.ShouldEqual(1);
                interceptEventClass.Delegates[0].ShouldEqual(action);

                interceptorProcessEvent.AddEventDelegate = null;
                interceptEventClass.TestEvent -= action;
                interceptorProcessEvent.AddEventDelegate.ShouldBeNull();
                action.ShouldEqual(interceptorProcessEvent.RemoveEventDelegate);
                interceptEventClass.Delegates.ShouldBeNull();
            }
        }

        [TestMethod]
        public void InterceptEventAsTargetTest()
        {
            using (var injector = GetInjector())
            {
                var target = new InterceptEventClass();
                var interceptorProcessEvent = new InterceptorProcessEvent { ProcessInTarget = true };
                InterceptionExtensionUtils.InterceptAsTarget(injector.Bind<InterceptEventClass>().ToConstant(target), interceptorProcessEvent);

                var interceptEventClass = injector.Get<InterceptEventClass>();
                var action = new Action(() => { });
                interceptEventClass.TestEvent += action;
                action.ShouldEqual(interceptorProcessEvent.AddEventDelegate);
                interceptorProcessEvent.RemoveEventDelegate.ShouldBeNull();
                interceptEventClass.Delegates.ShouldBeNull();
                target.Delegates.Length.ShouldEqual(1);
                target.Delegates[0].ShouldEqual(action);

                interceptorProcessEvent.AddEventDelegate = null;
                interceptEventClass.TestEvent -= action;
                interceptorProcessEvent.AddEventDelegate.ShouldBeNull();
                action.ShouldEqual(interceptorProcessEvent.RemoveEventDelegate);
                interceptEventClass.Delegates.ShouldBeNull();
                target.Delegates.ShouldBeNull();
            }
        }

        [TestMethod]
        public void InterceptPropertyTest()
        {
            using (var injector = GetInjector())
            {
                var interceptorProcessProperty = new InterceptorProcessProperty { ProcessOriginal = true };
                InterceptionExtensionUtils.Intercept(injector.Bind<InterceptPropertyClass>(), interceptorProcessProperty);

                var interceptPropertyClass = injector.Get<InterceptPropertyClass>();
                var action = new Action(() => { });
                interceptPropertyClass.Property = action;
                interceptorProcessProperty.SetValue.ShouldEqual(action);
                interceptorProcessProperty.GetValue.ShouldBeNull();
                interceptPropertyClass.Property.ShouldEqual(action);

                interceptorProcessProperty.SetValue = null;
                interceptorProcessProperty.GetValue = new object();
                object property = interceptPropertyClass.Property;
                interceptorProcessProperty.SetValue.ShouldBeNull();
                property.ShouldEqual(action);

                interceptorProcessProperty.GetValue = new object();
                interceptorProcessProperty.ProcessOriginal = false;
                property = interceptPropertyClass.Property;
                property.ShouldEqual(interceptorProcessProperty.GetValue);
            }
        }

        [TestMethod]
        public void InterceptPropertyAsTargetTest()
        {
            using (var injector = GetInjector())
            {
                var target = new InterceptPropertyClass();
                var interceptorProcessProperty = new InterceptorProcessProperty { ProcessInTarget = true };
                InterceptionExtensionUtils.InterceptAsTarget(injector.Bind<InterceptPropertyClass>().ToConstant(target), interceptorProcessProperty);

                var interceptPropertyClass = injector.Get<InterceptPropertyClass>();
                var action = new Action(() => { });
                interceptPropertyClass.Property = action;
                interceptorProcessProperty.SetValue.ShouldEqual(action);
                interceptorProcessProperty.GetValue.ShouldBeNull();
                target.Property.ShouldEqual(action);

                interceptorProcessProperty.SetValue = null;
                interceptorProcessProperty.GetValue = new object();
                object property = interceptPropertyClass.Property;
                interceptorProcessProperty.SetValue.ShouldBeNull();
                property.ShouldEqual(action);

                interceptorProcessProperty.GetValue = new object();
                interceptorProcessProperty.ProcessInTarget = false;
                property = interceptPropertyClass.Property;
                property.ShouldEqual(interceptorProcessProperty.GetValue);
            }
        }

        [TestMethod]
        public void InterceptMethodTest()
        {
            using (var injector = GetInjector())
            {
                var interceptorProcessMethod = new InterceptorProcessMethod();
                InterceptionExtensionUtils.Intercept(injector.Bind<InterceptMethodClass>(), interceptorProcessMethod);

                const string o = "st";
                interceptorProcessMethod.Result = o;
                var interceptMethodClass = injector.Get<InterceptMethodClass>();
                var action = new Action(() => { });
                var method = interceptMethodClass.Method(action, o);

                method.ShouldEqual(o);
                interceptorProcessMethod.MethodObjects.Length.ShouldEqual(2);
                interceptorProcessMethod.MethodObjects[0].ShouldEqual(action);
                interceptorProcessMethod.MethodObjects[1].ShouldEqual(o);

                interceptorProcessMethod.ProcessOriginal = true;
                method = interceptMethodClass.Method(action, o);

                method.ShouldEqual(InterceptMethodClass.Value);
                interceptorProcessMethod.MethodObjects.Length.ShouldEqual(2);
                interceptorProcessMethod.MethodObjects[0].ShouldEqual(action);
                interceptorProcessMethod.MethodObjects[1].ShouldEqual(o);
            }
        }

        [TestMethod]
        public void InterceptMethodAsTargetTest()
        {
            using (var injector = GetInjector())
            {
                var target = new InterceptMethodClass();
                var interceptorProcessMethod = new InterceptorProcessMethod();
                InterceptionExtensionUtils.InterceptAsTarget(injector.Bind<InterceptMethodClass>().ToConstant(target),
                                                             interceptorProcessMethod);

                const string o = "st";
                interceptorProcessMethod.Result = o;
                var interceptMethodClass = injector.Get<InterceptMethodClass>();
                var action = new Action(() => { });
                var method = interceptMethodClass.Method(action, o);

                method.ShouldEqual(o);
                interceptorProcessMethod.MethodObjects.Length.ShouldEqual(2);
                interceptorProcessMethod.MethodObjects[0].ShouldEqual(action);
                interceptorProcessMethod.MethodObjects[1].ShouldEqual(o);

                interceptorProcessMethod.ProcessOriginal = true;
                method = interceptMethodClass.Method(action, o);

                method.ShouldEqual(InterceptMethodClass.Value);
                interceptorProcessMethod.MethodObjects.Length.ShouldEqual(2);
                interceptorProcessMethod.MethodObjects[0].ShouldEqual(action);
                interceptorProcessMethod.MethodObjects[1].ShouldEqual(o);
            }
        }

        [TestMethod]
        public void InterceptorPriorityTest()
        {
            using (var injector = GetInjector())
            {
                var interceptorPriority1 = new InterceptorPriority { Priority = 0 };
                var interceptorPriority2 = new InterceptorPriority { Priority = 10 };
                var interceptorPriority3 = new InterceptorPriority { Priority = 20 };
                var interceptorPriority4 = new InterceptorPriority { Priority = 30 };
                InterceptionExtensionUtils.Intercept(injector.Bind<InterceptMethodClass>(), interceptorPriority4,
                                                     interceptorPriority1, interceptorPriority2, interceptorPriority3);
                int invokeCount = 0;
                Action<IList<IInterceptorProcess>> action = list =>
                                                                {
                                                                    list.Count.ShouldEqual(4);
                                                                    list[0].ShouldEqual(interceptorPriority4);
                                                                    list[1].ShouldEqual(interceptorPriority3);
                                                                    list[2].ShouldEqual(interceptorPriority2);
                                                                    list[3].ShouldEqual(interceptorPriority1);
                                                                    invokeCount++;
                                                                };
                interceptorPriority1.Action =
                    interceptorPriority2.Action =
                    interceptorPriority3.Action =
                    interceptorPriority4.Action = action;
                var interceptMethodClass = injector.Get<InterceptMethodClass>();
                interceptMethodClass.Method(null, null);
                invokeCount.ShouldEqual(4);
            }
        }

        [TestMethod]
        public void DefaultInterceptorActionTest()
        {
            using (var injector = GetInjector())
            {
                InterceptionExtensionUtils.Intercept(injector.Bind<DefaultInterceptorActionClass>(), new SimpleInterceptorProcessAdapter(new DefaultInterceptorAction()));
                var defaultInterceptorActionClass = injector.Get<DefaultInterceptorActionClass>();
                defaultInterceptorActionClass.I.ShouldEqual(0);
                defaultInterceptorActionClass.St.ShouldBeNull();

                const string stValue = "stValue";
                const int intValue = 100;
                int value = intValue;
                string st = stValue;
                Action action = () => { };

                defaultInterceptorActionClass = injector.Get<DefaultInterceptorActionClass>(new ConstructorParameter("st", stValue),
                                                            new ConstructorParameter("i", intValue));
                defaultInterceptorActionClass.I.ShouldEqual(intValue);
                defaultInterceptorActionClass.St.ShouldEqual(stValue);

                //Methods
                defaultInterceptorActionClass.PublicMethod(stValue).ShouldEqual(stValue);

                defaultInterceptorActionClass.CallProtectedMethod(ref value).ShouldEqual(intValue);
                value.ShouldEqual(DefaultInterceptorActionClass.DefaultValue);

                defaultInterceptorActionClass.InternalMethod(ref st).ShouldEqual(stValue);
                st.ShouldEqual(DefaultInterceptorActionClass.DefaultStValue);

                //Properties
                defaultInterceptorActionClass.Property = stValue;
                defaultInterceptorActionClass.Property.ShouldEqual(stValue);

                defaultInterceptorActionClass.ProtectedPropertyAccess = stValue;
                defaultInterceptorActionClass.ProtectedPropertyAccess.ShouldEqual(stValue);

                defaultInterceptorActionClass.InternalProperty = intValue;
                defaultInterceptorActionClass.InternalProperty.ShouldEqual(intValue);

                //Events
                defaultInterceptorActionClass.PublicEventDelegates.ShouldBeNull();
                defaultInterceptorActionClass.PublicEvent += action;
                defaultInterceptorActionClass.PublicEventDelegates.Length.ShouldEqual(1);
                defaultInterceptorActionClass.PublicEventDelegates[0].ShouldEqual(action);

                defaultInterceptorActionClass.PublicEvent -= action;
                defaultInterceptorActionClass.PublicEventDelegates.ShouldBeNull();

                defaultInterceptorActionClass.ProtectedEventDelegates.ShouldBeNull();
                defaultInterceptorActionClass.ProtectedEventAccess += action;
                defaultInterceptorActionClass.ProtectedEventDelegates.Length.ShouldEqual(1);
                defaultInterceptorActionClass.ProtectedEventDelegates[0].ShouldEqual(action);

                defaultInterceptorActionClass.ProtectedEventAccess -= action;
                defaultInterceptorActionClass.ProtectedEventDelegates.ShouldBeNull();

                defaultInterceptorActionClass.InternalEventDelegates.ShouldBeNull();
                defaultInterceptorActionClass.InternalEvent += action;
                defaultInterceptorActionClass.InternalEventDelegates.Length.ShouldEqual(1);
                defaultInterceptorActionClass.InternalEventDelegates[0].ShouldEqual(action);

                defaultInterceptorActionClass.InternalEvent -= action;
                defaultInterceptorActionClass.InternalEventDelegates.ShouldBeNull();
            }
        }

        [TestMethod]
        public void InterceptTest()
        {
            using (var injector = GetInjector())
            {
                var simpleInterceptor = new SimpleInterceptor();
                InterceptionExtensionUtils.Intercept(injector.Bind<InterceptTarget>(), simpleInterceptor);

                //no do
                simpleInterceptor.IsNoOpBehavior = true;
                const string value = "st";
                var inteceptTarget = injector.Get<InterceptTarget>();
                var method = inteceptTarget.Method(value);
                method.ShouldBeNull();

                inteceptTarget.Property = value;
                inteceptTarget.Property.ShouldBeNull();

                inteceptTarget.Event += () => { };
                inteceptTarget.GetEvents().ShouldBeNull();

                //
                simpleInterceptor.IsNoOpBehavior = false;
                method = inteceptTarget.Method(value);
                method.ShouldEqual(value);

                inteceptTarget.Property = value;
                inteceptTarget.Property.ShouldEqual(value);

                inteceptTarget.Event += () => { };
                inteceptTarget.GetEvents().ShouldNotBeEmpty();
            }

        }

        [TestMethod]
        public void InterceptAsTargetTest()
        {
            using (var injector = GetInjector())
            {
                var simpleInterceptor = new TargetInterceptor();

                //SingletonScope
                InterceptionExtensionUtils.InterceptAsTarget(injector.Bind<InterceptTarget>().ToSelf().InSingletonScope(),
                    new SimpleInterceptorProcessAdapter(simpleInterceptor));

                const int count = 100;
                for (int i = 0; i < count; i++)
                {
                    var interceptTarget = injector.Get<InterceptTarget>();
                    interceptTarget.Method(string.Empty);
                    interceptTarget.Property = string.Empty;
                }
                simpleInterceptor.Targets.Count.ShouldEqual(count * 2);
                var listId = new List<Guid>();
                foreach (InterceptTarget interceptTarget in simpleInterceptor.Targets)
                {
                    if (listId.Contains(interceptTarget.Id)) continue;
                    listId.Add(interceptTarget.Id);
                }
                listId.Count.ShouldEqual(1);

                //TransientScope
                simpleInterceptor.Targets.Clear();
                injector.Unbind<InterceptTarget>();
                InterceptionExtensionUtils.InterceptAsTarget(
                    injector.Bind<InterceptTarget>().ToSelf().InTransientScope(),
                    new SimpleInterceptorProcessAdapter(simpleInterceptor));

                for (int i = 0; i < count; i++)
                {
                    var interceptTarget = injector.Get<InterceptTarget>();
                    interceptTarget.Method(string.Empty);
                    interceptTarget.Property = string.Empty;
                }
                simpleInterceptor.Targets.Count.ShouldEqual(count * 2);
                listId.Clear();
                foreach (InterceptTarget interceptTarget in simpleInterceptor.Targets)
                {
                    if (listId.Contains(interceptTarget.Id)) continue;
                    listId.Add(interceptTarget.Id);
                }
                listId.Count.ShouldEqual(count);
            }
        }
    }
}